#include "EMDsteg.h"
#include "BitOperation.h"
#include <string>
EMDSteg::EMDSteg()
{

}

std::string EMDSteg::_2To5(int num)
{
    std::string ans{"00000000000000"};
    for(int i=13;num;--i){
        ans[i]=num%5+'0';
        num/=5;
    }
    return ans;
}

QImage EMDSteg::toGray(const QImage &colorImage)
{
    // 获取图像尺寸
    int width = colorImage.width();
    int height = colorImage.height();

    // 创建灰度图像
    QImage grayImage(width, height, QImage::Format_Grayscale8);

    // 转换为灰度图像
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            QColor color = colorImage.pixelColor(x, y);
            int grayValue = qGray(color.rgb());
            grayImage.setPixelColor(x, y, QColor(grayValue, grayValue, grayValue));
        }
    }
    return grayImage;
}

//嵌入一个5进制数
void EMDSteg::empImbeddedANum(uchar* imaData,char data,int& j){
    if(imaData[j]==255){
        --imaData[j];
    }else if(imaData[j]==0){
        ++imaData[j];
    }
    if(imaData[j+1]==255){
        --imaData[j+1];
    }else if(imaData[j+1]==0){
        ++imaData[j+1];
    }
    int tem1=0;
    uchar c1=imaData[j];
    memcpy(&tem1,&c1,1);
    int tem2=0;
    uchar c2=imaData[j+1];
    memcpy(&tem2,&c2,1);
    int f=(tem1%5+2*(tem2%5))%5;
    int s=data-'0';
    int r=mod(s-f,5);
    if(r==0)
    {
    }else if(r<=2){
        imaData[j+r-1]+=1;
    }else{
        imaData[j+(4-r)]-=1;
    }
    j+=2;
}

char EMDSteg::emdRetrievalANum(const uchar* imaData,int& j)
{
    int tem1=0;
    uchar c1=imaData[j];
    memcpy(&tem1,&c1,1);
    int tem2=0;
    uchar c2=imaData[j+1];
    memcpy(&tem2,&c2,1);
    j+=2;
    return (c1%5+2*(c2%5))%5+'0';
}
//嵌入信息
QImage EMDSteg::emdImbedded(const QByteArray &msg)
{
    if(msg.size()*8+28>m_carrierIamge.sizeInBytes()){
        return QImage();
    }
    int j=0;
    auto ima=m_carrierIamge;
    uchar* imaData=ima.bits();
    emdImbeddedInt(imaData,msg.size(),j);
    //嵌入信息
    for(int i=0;i<msg.size();++i){
        int len=0;
        auto c=msg[i];
        memcpy(&len,&c,1);
        auto data=_2To5(len);
        empImbeddedANum(imaData,data[10],j);
        empImbeddedANum(imaData,data[11],j);
        empImbeddedANum(imaData,data[12],j);
        empImbeddedANum(imaData,data[13],j);
    }
    return ima;
}

bool EMDSteg::emdImbedded(QImage &image, const QByteArray &msg)
{
    if(msg.size()*8+28>image.sizeInBytes()){
        return false;
    }
    int j=0;
    uchar* imaData=image.bits();
    //嵌入信息长度
    emdImbeddedInt(imaData,msg.size(),j);
    //嵌入信息
    for(int i=0;i<msg.size();++i){
        int tem=0;
        char c=msg[i];
        memcpy(&tem,&c,1);
        auto data=_2To5(tem);
        empImbeddedANum(imaData,data[10],j);
        empImbeddedANum(imaData,data[11],j);
        empImbeddedANum(imaData,data[12],j);
        empImbeddedANum(imaData,data[13],j);
    }
    return true;
}

bool EMDSteg::emdImbedded(uchar *image, int len, const QByteArray &msg)
{
    if(msg.size()*8+28>len){
        return false;
    }
    int j=0;
    uchar* imaData=image;
    //嵌入信息长度
    emdImbeddedInt(imaData,msg.size(),j);
    //嵌入信息
    for(int i=0;i<msg.size();++i){
        int tem=0;
        char c=msg[i];
        memcpy(&tem,&c,1);
        auto data=_2To5(tem);
        empImbeddedANum(imaData,data[10],j);
        empImbeddedANum(imaData,data[11],j);
        empImbeddedANum(imaData,data[12],j);
        empImbeddedANum(imaData,data[13],j);
    }
    return true;
}

QImage EMDSteg::emdImbedded(const QImage &image, const QByteArray &msg)
{
    if(image.format()!=QImage::Format_Indexed8)
    {
        QImage ans=toGray(image);
        emdImbedded(ans,msg);
        return ans;
    }else{
        QImage newImage=image;
        emdImbedded(newImage,msg);
        return newImage;
    }
}

QImage EMDSteg::emdImbedded(QImage &&image, const QByteArray &msg)
{
    if(msg.size()*8+28>image.sizeInBytes()){
        return QImage();
    }
    int j=0;
    uchar* imaData=image.bits();
    //嵌入信息长度
    emdImbeddedInt(imaData,msg.size(),j);
    //嵌入信息
    for(int i=0;i<msg.size();++i){
        int len=0;
        auto c=msg[i];
        memcpy(&len,&c,1);
        auto data=_2To5(len);
        empImbeddedANum(imaData,data[10],j);
        empImbeddedANum(imaData,data[11],j);
        empImbeddedANum(imaData,data[12],j);
        empImbeddedANum(imaData,data[13],j);
    }
    return image;
}

QByteArray EMDSteg::emdRetrieval()
{
    auto imaData=m_carrierIamge.bits();
    int j=0;
    int len=emdRetrievalInt(imaData,j);
    if(len<m_carrierIamge.sizeInBytes()-28){
        return QByteArray();
    }
    QByteArray ret{len,'\0'};
    for(int i=0;i<len;++i)
    {
        uchar d_5=0;
        for(int k=0;k<4;k++)
        {
            d_5=d_5*5+((imaData[j]%5+2*(imaData[j+1]%5))%5);
            j+=2;
        }
        memcpy(ret.data()+i,&d_5,1);
    }
    return ret;
}

QByteArray EMDSteg::emdRetrieval(const QImage &msg)
{
    auto imaData=msg.bits();
    int j=0;
    int len=emdRetrievalInt(imaData,j);
    if(len<msg.sizeInBytes()-28){
        return QByteArray();
    }
    QByteArray ret{len,'\0'};
    for(int i=0;i<len;++i)
    {
        int d_5=0;
        for(int k=0;k<4;k++)
        {
            int tem1=0;
            uchar c1=imaData[j];
            memcpy(&tem1,&c1,1);
            int tem2=0;
            uchar c2=imaData[j+1];
            memcpy(&tem2,&c2,1);
            d_5=d_5*5+((tem1%5+2*(tem2%5))%5);
            j+=2;
        }
        int l;
        memcpy(&l,&d_5,1);
        memcpy(ret.data()+i,&d_5,1);
    }
    return ret;
}

QByteArray EMDSteg::emdRetrieval(QImage &&msg)
{
    auto imaData=msg.bits();
    int j=0;
    int len=emdRetrievalInt(imaData,j);
    if(len<msg.sizeInBytes()-28){
        return QByteArray();
    }
    QByteArray ret{len,'\0'};
    for(int i=0;i<len;++i)
    {
        int d_5=0;
        for(int k=0;k<4;k++)
        {
            int tem1=0;
            uchar c1=imaData[j];
            memcpy(&tem1,&c1,1);
            int tem2=0;
            uchar c2=imaData[j+1];
            memcpy(&tem2,&c2,1);
            d_5=d_5*5+((tem1%5+2*(tem2%5))%5);
            j+=2;
        }
        memcpy(ret.data()+i,&d_5,1);
    }
    return ret;
}

QByteArray EMDSteg::emdRetrieval(const uchar *msg, int msg_len)
{
    auto imaData=msg;
    int j=0;
    int len=emdRetrievalInt(imaData,j);
    if(len==0||len>msg_len-28){
        return QByteArray();
    }
    QByteArray ret{len,'\0'};
    for(int i=0;i<len;++i)
    {
        int d_5=0;
        for(int k=0;k<4;k++)
        {
            int tem1=0;
            uchar c1=imaData[j];
            memcpy(&tem1,&c1,1);
            int tem2=0;
            uchar c2=imaData[j+1];
            memcpy(&tem2,&c2,1);
            d_5=d_5*5+((tem1%5+2*(tem2%5))%5);
            j+=2;
        }
        memcpy(ret.data()+i,&d_5,1);
    }
    return ret;
}

//嵌入一个整数
void EMDSteg::emdImbeddedInt(uchar* imaData, int msg, int &j)
{
    auto data=_2To5(msg);
    for(int i=0;i<14;++i){
        empImbeddedANum(imaData,data[i],j);
    }
}

//提取整数
int EMDSteg::emdRetrievalInt(const uchar* imaData, int &j)
{
    QString len{14,'0'};
    for(int i=0;i<14;++i)
    {
        len[i]=emdRetrievalANum(imaData,j);
    }
    return len.toInt(nullptr,5);
}
